﻿#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace Grawlix.Concurrency
{
	/// <summary>
	/// Encapsulates an operation completed sometime in the future.
	/// </summary>
	public class Future
	{
		/// <summary>
		/// The exception thrown.
		/// </summary>
		private Exception _failure;

		/// <summary>
		/// Whether the operation has been canceled.
		/// </summary>
		private bool _isCanceled;

		/// <summary>
		/// Whether the operation has completed.
		/// </summary>
		private bool _isFinished;

		/// <summary>
		/// Initializes a blank instance of the <see cref="Future"/>
		/// class.
		/// </summary>
		/// <remarks><see cref="Run"/> should be called manually.
		/// </remarks>
		protected Future()
		{
		}

		/// <summary>
		/// Runs the specified action on the thread pool.
		/// </summary>
		/// <param name="action">The action to run.</param>
		public Future(Action action)
			: this(new ThreadPoolExecutor(), action)
		{
		}

		/// <summary>
		/// Runs the specified action on a specified executor.
		/// </summary>
		/// <param name="executor">The executor used to run the action.
		/// </param>
		/// <param name="action">The action to run.</param>
		public Future(IExecutor executor, Action action)
		{
			Run(executor, action);
		}

		/// <summary>
		/// Gets the exception thrown during operation, if any.
		/// </summary>
		/// <value>The exception thrown during the operation or 
		/// <c>null</c>.
		/// </value>
		/// <exception cref="InvalidOperationException">Attempt to
		/// access the failure result of a future before the future
		/// has finished running.</exception>
		public Exception Failure
		{
			get
			{
				if (!IsFinished)
				{
					throw new InvalidOperationException(
						"Attempt to access the failure result of " +
						"a future before the future has finished running. " +
						"You should check IsFinished before " +
						"the failure result.");
				}
				return _failure;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the operation has been
		/// canceled.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if the operation has been canceled; otherwise,
		/// 	
		/// 	<c>false</c>.
		/// </value>
		public bool IsCanceled
		{
			get
			{
				Thread.MemoryBarrier();
				return _isCanceled;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the operation has finished
		/// running.
		/// </summary>
		/// <value>
		/// <c>true</c> if the operation has finished running;
		/// otherwise, 
		/// <c>false</c>.
		/// </value>
		/// <remarks>Finished does not mean successful. The operation
		/// could have been canceled or failed.</remarks>
		public bool IsFinished
		{
			get
			{
				Thread.MemoryBarrier();
				return _isFinished;
			}
		}

		/// <summary>
		/// Cancels the operation.
		/// </summary>
		/// <remarks>It is OK to call this method multiple times.
		/// </remarks>
		public void Cancel()
		{
			_isCanceled = true;
			Thread.MemoryBarrier();
		}

		/// <summary>
		/// Ensures the success of the operation, or throws an
		/// exception.
		/// </summary>
		/// <exception cref="InvalidOperationException">Attempt to
		/// access the result of a future before the future has
		/// finished running.</exception>
		/// <exception cref="Exception">Failure, if not <c>null</c>.
		/// </exception>
		protected void EnsureSuccess()
		{
			if (!IsFinished)
			{
				throw new InvalidOperationException(
					"Attempt to access the result of " +
					"a future before the future has finished running. " +
					"You should check IsFinished before the result.");
			}
			if (_failure != null)
				throw _failure;
		}

		/// <summary>
		/// Creates a new future, executing the given action.
		/// </summary>
		/// <param name="action">The action to execute.</param>
		/// <returns>The new <see cref="Future"/> object.</returns>
		public static Future Fork(Action action)
		{
			return new Future(action);
		}

		/// <summary>
		/// Creates a new future, executing the given action.
		/// </summary>
		/// <typeparam name="T">The return type of the action.
		/// </typeparam>
		/// <param name="action">The action to execute.</param>
		/// <returns>A new <see cref="Future{T}" /> object.</returns>
		public static Future<T> Fork<T>(Func<T> action)
		{
			return new Future<T>(action);
		}

		/// <summary>
		/// Creates a new future, executing the given action.
		/// </summary>
		/// <param name="executor">The executor used to run the action.
		/// </param>
		/// <param name="action">The action to execute.</param>
		/// <returns>The new <see cref="Future"/> object.</returns>
		public static Future Fork(IExecutor executor,
		                          Action action)
		{
			return new Future(executor, action);
		}

		/// <summary>
		/// Creates a new future, executing the given action.
		/// </summary>
		/// <typeparam name="T">The return type of the action.
		/// </typeparam>
		/// <param name="executor">The executor used to run the action.
		/// </param>
		/// <param name="action">The action to execute.</param>
		/// <returns>A new <see cref="Future{T}" /> object.</returns>
		public static Future<T> Fork<T>(IExecutor executor,
		                                Func<T> action)
		{
			return new Future<T>(executor, action);
		}

		/// <summary>
		/// Runs the specified action on the specified executor.
		/// </summary>
		/// <param name="executor">The executor used to run the action.
		/// </param>
		/// <param name="action">The action to run.</param>
		[SuppressMessage("Microsoft.Design", "CA1031",
			Justification =
				"Exception is propagated via another route.")]
		protected void Run(IExecutor executor, Action action)
		{
			executor.Execute(() =>
				{
					if (IsCanceled)
					{
						_failure =
							new ConcurrentException(
								"A future operation was canceled before it " +
								"could start.");
					}
					else
					{
						try
						{
							action();
						}
						catch (CancellationException e)
						{
							_isCanceled = true;
							_failure =
								new ConcurrentException(
									"A future operation was canceled " +
									"internally by throwing a " +
									"CancellationException.", e);
						}
						catch (Exception e)
						{
							_failure = e;
						}
					}
					Thread.MemoryBarrier();
					_isFinished = true;
					Thread.MemoryBarrier();
				});
		}
	}

	/// <summary>
	/// Encapsulates an operation completed sometime in the future
	/// and saves its result.
	/// </summary>
	/// <typeparam name="T">The result type.</typeparam>
	public class Future<T> : Future
	{
		/// <summary>
		/// The result of the operation.
		/// </summary>
		private T _result;

		/// <summary>
		/// Initializes a new instance of the 
		/// <see cref="Future&lt;T&gt;"/> class.
		/// </summary>
		/// <remarks><see cref="Run"/> should be called manually.
		/// </remarks>
		protected Future()
		{
		}

		/// <summary>
		/// Runs the specified action on the thread pool.
		/// </summary>
		/// <param name="action">The action to run.</param>
		public Future(Func<T> action)
			: this(new ThreadPoolExecutor(), action)
		{
		}

		/// <summary>
		/// Runs the specified action on a specified executor.
		/// </summary>
		/// <param name="executor">The executor used to run the action.
		/// </param>
		/// <param name="action">The action to run.</param>
		public Future(IExecutor executor, Func<T> action)
		{
			Run(executor, action);
		}

		/// <summary>
		/// Gets the result of the operation or rethrows its exception.
		/// </summary>
		/// <value>The result of the operation.</value>
		public T Result
		{
			get
			{
				EnsureSuccess();
				return _result;
			}
		}

		/// <summary>
		/// Runs the specified function on the specified executor.
		/// </summary>
		/// <param name="executor">The executor used to run the function.
		/// </param>
		/// <param name="action">The function to run.</param>
		protected void Run(IExecutor executor, Func<T> action)
		{
			Run(executor, () =>
				{
					_result = action();
					return;
				});
		}
	}
}